//
// Created by lz on 3/6/21.
//

#include "net/TcpServer.h"

#include "base/Logging.h"
#include "net/Acceptor.h"
#include "net/EventLoop.h"
#include "net/EventLoopThreadPool.h"
#include "net/SocketsOps.h"

#include <cstdio>   // snprintf

using namespace lzweb;

using namespace lzweb::net;

TcpServer::TcpServer(EventLoop* loop,
	const InetAddress& listenAddr,
	const string& nameArg,
	TcpServer::Option option)
	: loop_(CHECK_NOTNULL(loop)),
	  ipPort_(listenAddr.toIpPort()),
	  name_(nameArg),
	  acceptor_(new Acceptor(loop, listenAddr, option = kReusePort)),
	  threadPool_(new EventLoopThreadPool(loop, name_)),
	  connectionCallback_(defaultConnectionCallback),
	  messageCallback_(defaultMessageCallback),
	  nextConnId_(1)
{
	acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, _1, _2));
}
TcpServer::~TcpServer()
{
	loop_->assertInLoopThread();
	LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing";
	for(auto& item: connections_)
	{
		TcpConnectionPtr conn(item.second);
		//减掉map中的引用
		item.second.reset();
		conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));

	}
}



void TcpServer::setThreadNum(int numThreads)
{
	assert(0 <=numThreads);
	threadPool_->setThreadNum(numThreads);
}
void TcpServer::start()
{
	//确保TcpServer没有开始
	if (started_.getAndSet(1) == 0)
	{
		threadPool_->start(threadInitCallback_);
		assert(!acceptor_->listening());
		loop_->runInLoop(std::bind(&Acceptor::listen,
			get_pointer(acceptor_)));
	}

}

void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
	loop_->assertInLoopThread();
	EventLoop* ioLoop = threadPool_->getNextLoop();
	char buf[64];
	snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
	++nextConnId_;
	string connName = name_ + buf;

	LOG_INFO << "TcpServer::newConnection [" << name_
			<< "] - new connection [" << connName
			<< "] from "<<peerAddr.toIpPort();
	// getLocalAddr 能通过sockfd获取其本地绑定的地址
	InetAddress localAddr(sockets::getLocalAddr(sockfd));
	// FIXME poll with zero timeout to double confirm the new connection
	// FIXME use make_shared if necessary
	TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd,
		localAddr, peerAddr));
	connections_[connName] = conn;
	conn->setConnectionCallback(connectionCallback_);
	conn->setMessageCallback(messageCallback_);
	conn->setWriteCompleteCallback(writeCompleteCallback_);
	conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, _1)); //FIXME: unsafe
	ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

//此函数不一定要在所属线程运行
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
	//FIXME: unsafe
	loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

//InLoop函数必须保证其在EventLoop在所属的线程执行
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
	loop_->assertInLoopThread();
	LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
			 << "] - connection " << conn->name();

	size_t n = connections_.erase(conn->name());
	(void) n;
	assert( n == 1);
	EventLoop* ioLoop = conn->getLoop();
	ioLoop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));

}
